#include <linux/fb.h>
#include "mpi_lib.h"
#include "sample_osd_vo.h"

//#define USE_FRAMEBUFFER

typedef union __argb8888
{
    unsigned int data32;
    struct __bit32s
    {
        unsigned int b: 8;
        unsigned int g: 8;
        unsigned int r: 8;
        unsigned int a: 8;
    } bits;
} argb8888_u;

typedef union __argb4444
{
    unsigned short data16;
    struct __bit16s
    {
        unsigned short b: 4;
        unsigned short g: 4;
        unsigned short r: 4;
        unsigned short a: 4;
    } bits;
} argb4444_u;


static int Argb8888ToAyuv4444Init(unsigned short *pLUT)
{
    unsigned int  index;
    unsigned char a, r, g, b;
    unsigned char  y, u, v;

    for(index = 0; index < (0x1 << 16); index++)
    {
        a = (index & 0xF000) >> 12;
        r = (index & 0x0F00) >> 4;
        g = (index & 0x00F0) >> 0;
        b = (index & 0x000F) << 4;
        y = ((66 * r + 129 * g +  25 * b + 128) >> 8)  + 16;
        u = ((-38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
        v = ((112 * r -  94 * g -  18 * b + 128) >> 8) + 128;
        y >>= 4;
        u >>= 4;
        v >>= 4;
        pLUT[index] = ((a << 12) | (y << 8) | (u << 4) | (v << 0));
    }

    return 0;
}

static void Argb8888ConvertToAyuv4444(unsigned short *pLUT,
                                      unsigned int *pSrc,
                                      unsigned short *pDst,
                                      unsigned int width,
                                      unsigned int height)
{
    u32 i;
    u32        pixel;
    argb8888_u argb8888;
    argb4444_u argb4444;

    pixel = width * height;

    for(i = 0; i < pixel; i++)
    {
        argb8888.data32 = pSrc[i];
        argb4444.bits.a = argb8888.bits.a >> 4;
        argb4444.bits.r = argb8888.bits.r >> 4;
        argb4444.bits.g = argb8888.bits.g >> 4;
        argb4444.bits.b = argb8888.bits.b >> 4;
        pDst[i] = pLUT[argb4444.data16];
    }
}

int OsdVoInit(void)
{
    int ret;
    sgks_mpi_osd_voParamSetup_s osdVoParam;

    ret = sgks_mpi_Osd_Open(1);
    if(ret != SGKS_SUCCESS)
    {
        printf("Error: sgks_mpi_Osd_VoOpen, ret=%d\n", ret);
        return 1;
    }

    osdVoParam.vo_id = 0;
    osdVoParam.en    = 1;
    osdVoParam.flip = 0;
    osdVoParam.winWidth = 800;
    osdVoParam.winHeight = 480;
    osdVoParam.winOffsetX = 0;
    osdVoParam.winOffsetY = 0;
    osdVoParam.rescalerEn = 0;
    osdVoParam.rescalerInputWidth = 0;
    osdVoParam.rescalerInputHeight = 0;
    osdVoParam.src = SGKS_MPI_OSD_VO_DIRECT_IN;
    osdVoParam.directMode = SGKS_MPI_OSD_VO_DIRECT_MODE_4444_AYUV;
    sgks_mpi_Osd_VoParamSet(&osdVoParam);
    return 0;
}

static void *OsdVoShow(void *args)
{
#define RGB_FILE1  "/home/yuv/800x480.argb"
#define RGB_FILE2  "/home/yuv/800x480.argb"

    int fd[2];
    unsigned int width, height, pixel, bytePerPixel, fileSize, rLen;
    unsigned char *readBuf[2];
    unsigned short *pLUT;
    sgks_mpi_mem_s meminfo[2];
    int sw = 0;

    width = 800;
    height = 480;
    pixel = width * height;
    bytePerPixel = 4;
    fileSize = pixel * bytePerPixel;

    readBuf[0] = (unsigned char *)sgks_mpi_Malloc(fileSize*2);
    readBuf[1] = readBuf[0] + fileSize;

    fd[0] = open(RGB_FILE1, O_RDONLY);
    if(fd[0] <= 0)
    {
        printf("Error: open %s\n", RGB_FILE1);
        return NULL;
    }
    rLen = read(fd[0], readBuf[0], fileSize);
    printf("open %s file size: %d ok\n", RGB_FILE1, rLen);

    fd[1] = open(RGB_FILE2, O_RDONLY);
    if(fd[1] <= 0)
    {
        printf("Error: open %s\n", RGB_FILE2);
        return NULL;
    }
    rLen = read(fd[1], readBuf[1], fileSize);
    printf("open %s file size: %d ok\n", RGB_FILE1, rLen);

#ifdef USE_FRAMEBUFFER
    {
        int fbfd;
        struct fb_var_screeninfo vinfo;
        struct fb_fix_screeninfo finfo;
        unsigned char *fbbuf;

        //open display device
        fbfd = open("/dev/fb0", O_RDWR);
        if (fbfd < 0)
        {
            printf("Error: cannot open framebuffer device.\n");
            return NULL;
        }

        if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
        {
            printf("Error: reading fixed information.\n");
            return NULL;
        }

        if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
        {
            printf("Error: reading variable information.\n");
            return NULL;
        }

        printf("R:%d,G:%d,B:%d \n", vinfo.red, vinfo.green, vinfo.blue);
        printf("%dx%d, %dbpp\n",    vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
        usleep(400*1000);

        //get object memory
        fbbuf = (char *)mmap(0, width*height*4, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
        if ((int)fbbuf == -1)
        {
            printf("Error: failed to map framebuffer device to memory.\n");
            return NULL;
        }

        while(1)
        {
            printf("*********fb:%d\n", sw);
            sw = (sw==0) ? 1:0;
            memcpy(fbbuf, readBuf[sw], 800*480*4);
            vinfo.yoffset = 0;
            if(ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo) < 0)
            {
                printf("Cannot display Pan\n.");
                return NULL;
            }
            usleep(1000*1000);
        }
    }
#else
    pLUT = (unsigned short *)sgks_mpi_Malloc((1<<16)*2);
    Argb8888ToAyuv4444Init(pLUT);

    sgks_mpi_Osd_Mallc(&meminfo[0], pixel*2);
    printf("meminfo.user_addr:%p\n", meminfo[0].user_addr);
    printf("meminfo.phy_addr:%p\n",  meminfo[0].phy_addr);
    printf("meminfo.size:%u\n",      meminfo[0].size);

    sgks_mpi_Osd_Mallc(&meminfo[1], pixel*2);
    printf("meminfo.user_addr:%p\n", meminfo[1].user_addr);
    printf("meminfo.phy_addr:%p\n",  meminfo[1].phy_addr);
    printf("meminfo.size:%u\n",      meminfo[1].size);

    while(1)
    {
        printf("*********%d\n", sw);
        printf("pLUT:%p\n", pLUT);
        printf("readBuf[sw]:%p\n", readBuf[sw]);
        printf("meminfo[sw].user_addr:%p\n", meminfo[sw].user_addr);
        printf("width:%u\n", width);
        printf("width:%u\n", height);
        Argb8888ConvertToAyuv4444(pLUT, (unsigned int *)readBuf[sw], (unsigned short *)meminfo[sw].user_addr, width, height);
        sgks_mpi_Osd_VoUpdateDisplay(&meminfo[sw]);
        usleep(3000 * 1000);
        sw = !sw;
    }
#endif
}

int Osd_VoTest()
{
    int ret = SGKS_SUCCESS;

    ret = OsdVoInit();
    if(ret != SGKS_SUCCESS)
    {
        printf("Error: OsdVoInit, ret=%d\n", ret);
        return ret;
    }

    pthread_t pVoOsdThread;
    if(pthread_create(&pVoOsdThread, NULL, OsdVoShow, NULL) != 0)
    {
        DEBUGMSG(ZONE_ERROR, ("YUV ENCODE THREAD CREATE FAIL\n"));
        ret = SGKS_ERR_OSD_PARAMERR;
    } 
      
    return ret;
}
